#!/usr/bin/env python
# coding: utf-8

# In[ ]:


# 导入模块需要发送HTTP请求、数据加密、HTML解析、数据编码、JSON处理、时间处理和SQLite数据库操作
import requests
from Crypto.Cipher import AES
from lxml import etree
from binascii import b2a_base64
import json
import time
import sqlite3

# 请求头
headers = {
    'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.69 Safari/537.36'
}

# 固定参数
e = '010001'
f = '00e0b509f6259df8642dbc35662901477df22677ec152b5ff68ace615bb7b725152b3ab17a876aea8a5aa76d2e417629ec4ee341f56135fccf695280104e0312ecbda92557c93870114af6c9d05c4f7f0c3685b7a46bee255932575cce10b424d813cfe4875d3e82047b97ddef52741d546b8e289dc6935b3ece0462db0a22b8e7'

g = '0CoJUm6Qyw8W8jud'
# 调试浏览器后得出的一个随机值i
i = 'ELDpodnrvfXdBx8u'

def get_163():
    """
    发送HTTP请求获取网易云音乐热歌榜的HTML，然后使用XPath解析出歌曲名称和id，调用函数获取歌曲的热门评论
    """
    # 热歌榜URL
    toplist_url = 'https://music.163.com/discover/toplist?id=3778678'
    # 模拟浏览器，发送get请求
    response = requests.get(toplist_url, headers=headers)
    html = response.content.decode()
    
    # xpath定位到歌曲信息的li列表
    html = etree.HTML(html)
    namelist = html.xpath("//div[@id='song-list-pre-cache']/ul[@class='f-hide']/li")
    
    for name in namelist:
        # 提取a标签的文本作为歌名
        song_name = name.xpath('./a/text()')[0]
        
        # 提取a标签的 href 属性的split('=')[1]提取歌曲的id
        song_id = name.xpath('./a/@href')[0].split('=')[1]
        # 调用函数获取热评
        content = get_hotConmments(song_id)
        print(song_name, song_id)
        # 歌曲信息存到数据库
        save_sqlite(song_id, song_name, content)
        
# 与i对应的加密参数encSecKey
def get_encSecKey():
    encSecKey = "abffe0a0c44d53b50966c582333b97a353defa05882d083379ce965f18c68f0b33c60ef626efb071ef6737ba2b5d0a09ea5d5b8a888a624f60639dbd9e556ff2ab3833b0fa9bf96370addf494bf11c17a4c8a5ccdebcf935f403653f1ec686b09279c1c401e2d52371a54b82f5a24b46f826ea372077d092351b49a8b76183fc"

    return encSecKey

# 仿写js功能
# 仿写两次调用b函数
def get_params(data):
    first = enconda_params(data, g)
    second = enconda_params(first, i)

    return second

# 仿写js实现AES算法
# 加密params
def enconda_params(data, key):
    d = 16 - len(data) % 16
    data += chr(d) * d
    data = data.encode('utf-8')
    aes = AES.new(key=key.encode('utf-8'), IV='0102030405060708'.encode('utf-8'), mode=AES.MODE_CBC)
    bs = aes.encrypt(data)
    # b64解码
    params = b2a_base64(bs).decode('utf-8')
    return params

# 获取热评
def get_hotConmments(id):
    data = {
        'cursor': '-1',
        'offset': '0',
        'orderType': '1',
        'pageNo': '1',
        'pageSize': '20',
        'rid': f'R_SO_4_{id}',
        'threadId': f'R_SO_4_{id}'
    }
    post_data = {
        'params': get_params(json.dumps(data)),
        'encSecKey': get_encSecKey()
    }
    song_url = 'https://music.163.com/weapi/comment/resource/comments/get?csrf_token=ce10dc34c626dc6aef3e07c86be16d70'

    response = requests.post(url=song_url, data=post_data, headers=headers)
    json_dict = json.loads(response.content)
    
    hotcontent = {}
    for content in json_dict['data']['hotComments']:
        content_text = content['content']
        content_id = content['user']['nickname']
        hotcontent[content_id] = content_text

    return hotcontent

# 保存到SQLite数据库


def save_sqlite(song_id, song_name, content):
    try:
        conn = sqlite3.connect('C:\\Users\\86152\\wangyi1.db', detect_types=sqlite3.PARSE_DECLTYPES | sqlite3.PARSE_COLNAMES)
        cursor = conn.cursor()

        # 检查表是否存在，如果不存在，则创建它
        cursor.execute('''
            CREATE TABLE IF NOT EXISTS music_163 (
                song_id INTEGER,
                song_name TEXT,
                comments TEXT,
                nickname TEXT
            )
        ''')

        sql = """
            INSERT INTO music_163(song_id, song_name, comments, nickname)
            VALUES (?, ?, ?, ?)
        """
        for nickname in content:
            data = (int(song_id), song_name, content[nickname], nickname)
            cursor.execute(sql, data)
            conn.commit()

        conn.close()
    except sqlite3.Error as e:
        print(f"SQLite 错误: {e}")

# 示例用法
# save_sqlite(song_id, song_name, content)

if __name__ == '__main__':
    get_163()

